<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>工程遥测</title>
  <link rel="stylesheet" href="/static/css/bootstrap.min.css">
  <link rel="stylesheet" href="/static/css/font-awesome.min.css">
  <link rel="stylesheet" href="/static/css/style.css">
  <!--  <script src="/static/js/echarts.5.5.js"></script>-->
  <script src="/static/js/echarts.4.9.js"></script>
  <style>
    th, td {
      min-width: 100px !important;
      word-break: keep-all;
      white-space: pre
    }

    td > kbd {
      background: #eee;
      color: #888
    }

    #c > div + div {
      overflow: hidden !important;
    }

    .E {
      color: red !important;
    }

    .table.small tr > * {
      padding: 3px 6px
    }

    .loading-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(255, 255, 255, 0.8);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 9999;
      font-size: 18px;
    }

    .loading-spinner {
      border: 4px solid #f3f3f3;
      border-top: 4px solid #3498db;
      border-radius: 50%;
      width: 40px;
      height: 40px;
      animation: spin 1s linear infinite;
      margin-right: 10px;
    }

    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }

    .alert {
      margin-top: 10px;
      padding: 8px 12px;
      border-radius: 4px;
    }

    .alert-danger {
      color: #721c24;
      background-color: #f8d7da;
      border: 1px solid #f5c6cb;
    }

  </style>
</head>
<body>
<h2>工程遥测</h2>
<div class="bg-light">

  <label for="station">站:</label>
  <select id="station" onchange="stationChange()"></select>
  <label for="packages" class="ps-3">载荷:</label>
  <select id="packages"></select>
  <label class="ps-3" for="tstart">时间(<small>UTC</small>):</label>
  <input id="tstart" type="datetime-local"  onchange="validateTimeRange()">
  ~<input id="tend" type="datetime-local" onchange="validateTimeRange()">
  <button id="queryBtn" class="btn btn-primary btn-sm ms-2" onclick="manualQuery()">
    <i class="fa fa-search"></i>查询
  </button>
  <label for="frames" class="ps-3" id="frames_sidx">起始位置:</label>
  <input type="range" min="0" value="0" max="0" id="frames" style="width: 400px"><span> </span>
  <a class="btn btn-primary btn-sm" onclick="current_packages.to_csv($prm.val(),this)"><i
    class="fa fa-cloud-download"></i>下载</a>
  <div id="timeAlert" style="display: none;"></div>
</div>

<!-- Loading overlay -->
<div id="loadingOverlay" class="loading-overlay" style="display: none;">
  <div class="loading-spinner"></div>
  <span>正在查询数据...</span>
</div>

<select id="params" size="20" multiple
        style="outline: none;border: 1px solid lightgrey;height: calc(100vh - 80px);width: 350px;"></select>
<div style="position: absolute;top: 80px;bottom: 380px;left: 350px;right: 0;overflow: auto;" class="bg-light">
  <table id="result" class="table table-bordered table-hover small m-0">
    <thead></thead>
    <tbody></tbody>
  </table>
</div>
<div id="c"
     style="overflow: hidden;height: 380px;position: absolute;bottom: 0;right: 0;left: 350px;border-top: 1px solid lightgrey"
     class="bg-light"></div>
<script src="/static/js/jquery-3.6.0.min.js"></script>
<script src="/static/js/xtool.js"></script>
<script src="times.js"></script>
<script>
  const local_offset = new Date().getTimezoneOffset() * 60e3;
  var _tfmt = (tms) => { return tms.toISOString().slice(0, 19).replace('T', ' ')};
  let NLINE = 16;
  // 图多分类
  let CLIMT = 9
  let chart = echarts.init($$('#c'), null, {devicePixelRatio: 2, renderer: 'canvas'})
  option = {
    useUTC: true,
    color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
    xAxis: {type: 'time'},
    yAxis: {type: 'value', scale: true},
    legend: {right: 50, top: 15},
    toolbox: {
      top: 15,
      right: 10,
      orient: 'vertical',
      feature: {saveAsImage: {type: 'png', pixelRatio: 5}, dataZoom: {yAxisIndex: false}}
    },
    grid: {top: 40, left: 80, right: 50, bottom: 70},
    tooltip: {
      trigger: 'axis', axisPointer: {type: 'line', axis: 'x'},
      position: (pos, params, el, elRect, size) => {
        chart.curDataIndex = params[0].dataIndex;
      },
      extraCssText: 'background-color: #fff9;color: #444;border: 1px solid #aaa'
    }, animation: false,
    dataZoom: [
      {type: 'slider', height: 20, xAxisIndex: 0, show: true},
      {type: 'inside', xAxisIndex: 0,}]
  }
  // chart.showLoading()
  // chart.setOption(option)
  let q
  let db = new Inflex()
  let data;
  $pkg = $('#packages');
  $prm = $('#params');
  $frm = $('#frames');
  $pgs = $('#frames+*');
  $TH = $('#result>thead');
  $TB = $('#result>tbody');
  $TS = $$('#tstart')
  $TE = $$('#tend')
  $timeAlert = $('#timeAlert')
  $queryBtn = $('#queryBtn')
  $loadingOverlay = $('#loadingOverlay')
  $station = $('#station')
  $frm[0].step = NLINE;

  // 显示loading
  function showLoading() {
    $loadingOverlay.show();
    $queryBtn.prop('disabled', true);
  }

  // 隐藏loading
  function hideLoading() {
    $loadingOverlay.hide();
    $queryBtn.prop('disabled', false);
  }

  // 显示错误提示
  function showAlert(message, type = 'danger') {
    $timeAlert.removeClass('alert-danger alert-warning alert-success')
              .addClass(`alert alert-${type}`)
              .html(message)
              .show();
    setTimeout(() => {
      $timeAlert.hide();
    }, 5000);
  }

  // 验证时间范围（最多20天）
  function validateTimeRange() {
    if ($TS.value && $TE.value) {
      const startTime = new Date($TS.value);
      const endTime = new Date($TE.value);
      const timeDiff = endTime - startTime;
      const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
      
      if (daysDiff > 20) {
        showAlert('时间范围不能超过20天，请重新选择时间范围！', 'danger');
        return false;
      }
      if (daysDiff < 0) {
        showAlert('结束时间不能早于开始时间！', 'danger');
        return false;
      }
      $timeAlert.hide();
    }
    return true;
  }

  // 手动查询函数
  async function manualQuery() {
    // 验证时间范围
    if (!validateTimeRange()) {
      return;
    }

    if (!$TS.value || !$TE.value) {
      showAlert('请选择开始时间和结束时间！', 'danger');
      return;
    }

    showLoading();
    
    try {
      // 设置60秒超时
      const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error('请求超时')), 60000);
      });

      const queryPromise = queryData();
      
      await Promise.race([queryPromise, timeoutPromise]);
      
      showtable();
      display_chart();
      
    } catch (error) {
      console.error('查询失败:', error);
      if (error.message === '请求超时') {
        showAlert('查询超时(60s)，请缩小时间范围后重试！', 'danger');
      } else if (error.message === '用户取消查询') {
        // 用户主动取消，不显示错误
        return;
      } else {
        showAlert('查询失败，请检查网络连接或稍后重试！', 'danger');
      }
    } finally {
      hideLoading();
    }
  }

  // 站切换函数
  function stationChange() {
    // 站切换时清空数据显示
    $TB.html(`<tr><td style="color: #999" colspan="10">请点击查询按钮获取数据</td></tr>`)
    chart.clear()
  }

  // 初始化站列表
  async function initStations() {
    try {
      // 动态获取当前站
      const currentStation = getStation();
      
      // 先添加当前站作为选项
      if (currentStation) {
        $station.html(`<option value="${currentStation}">${currentStation}站</option>`);
        $station.val(currentStation);
      } else {
        // 如果无法获取当前站，添加默认选项
        $station.html('<option value="">请选择站</option>');
      }
      
      // 可以在这里添加从API获取完整站列表的逻辑
      // 例如：const stations = await $api('omsp.stations');
      
    } catch (e) {
      console.warn('无法获取当前站信息：', e);
      // 添加通用站选项作为后备
      $station.html(`
        <option value="all">全部站</option>
        <option value="GRAS3">GRAS3</option>
        <option value="GRAS4">GRAS4</option>
        <option value="GRAS5">GRAS5</option>
        <optgroup label="测控站">
          <option value="BACC_JM">佳木斯深空站</option>
          <option value="BACC_KS">喀什深空站</option>
          <option value="BACC_NM">阿根廷深空站</option>
          <option value="BACC_QD18">青岛18米天线</option>
          <option value="BACC_KS18">喀什18米天线</option>
          <option value="BACC_NMBY">纳米比亚18米天线</option>
          <option value="BACC_KSSYN">喀什合成站</option>
          <option value="BACC_KS35">喀什35米天线</option>
        </optgroup>
      `);
    }
  }

  window.onload = async function () {
    // 初始化站列表
    await initStations();

    if ($TS.value == '') {
      let cur_time = new Date()
      $TS.value = _tfmt(new Date(cur_time.getTime() - 10 * 60 * 1e3));
      $TE.value = _tfmt(cur_time)
    }

    q = await fetch('pkg_list.json').then(v => v.json())
    // let ss = q.subsys
    // $pkg.html(Object.keys(ss).map(s => {
    //   return $(`<optgroup label="${ss[s]}"></optgroup>`).html(q.pkgs.filter(v => v.sid === s).map(v => {
    //     r = document.createElement('option')
    //     r.value = v.id;
    //     r.innerHTML = `${v.name} / 0x${v.id.slice(0, 4).toUpperCase()} ${v.id.slice(4).toUpperCase()}`;
    //     return r;
    //   }))
    // }))
    q.pkgs.sort((a, b) => a.id - b.id)
    $pkg.html(q.pkgs.map(v => {
      r = document.createElement('option')
      r.value = v.id;
      r.innerHTML = `${v.name} / 0x${v.id.slice(0, 4).toUpperCase()} ${v.id.slice(4).toUpperCase()}`;
      return r;
    }))
    pkg_change();

  }
  var current_packages

  async function queryData() {
    let pkg = $pkg.val();
    let T0 = ($TS.valueAsNumber) * 1e6;
    let T1 = ($TE.valueAsNumber) * 1e6;
    // 获取多选参数
    let selected = $prm.val();
    let columns = ['time'];
    if (selected && selected.length > 0) {
      columns = ['time', ...selected];
    } else {
      columns = ['*'];
    }
    // 判断station是否为all
    let stationVal = $station.val() || getStation();
    // 先检查数据量
    let countSql;
    if (stationVal === 'all') {
      countSql = `select count(*) as cnt from "${pkg}" where time >${T0} and time < ${T1}`;
    } else if (stationVal.startsWith('BACC_')) {
      const stationList = [`${stationVal}`, `${stationVal}_C`, `${stationVal}_D`];
      const stationCond = stationList.map(s => `station='${s}'`).join(' or ');
      countSql = `select count(*) as cnt from "${pkg}" where time >${T0} and time < ${T1} and (${stationCond})`;
    } else {
      countSql = `select count(*) as cnt from "${pkg}" where time >${T0} and time < ${T1} and station = '${stationVal}'`;
    }
    
    let countResult = await db.execute(countSql);
    let rowCount = countResult[0]?.cnt || 0;
    
    if (rowCount > 50000) {
      if (!confirm(`查询将返回 ${rowCount.toLocaleString()} 条记录，数据量过大有崩溃风险！\n\n强烈建议缩短时间段。\n\n确定要继续查询吗？`)) {
        throw new Error('用户取消查询');
      }
    }
    
    // 执行实际查询
    let sql;
    if (stationVal === 'all') {
      sql = `select ${columns.join(', ')} from "${pkg}" where time >${T0} and time < ${T1} order by time`;
    } else if (stationVal.startsWith('BACC_')) {
      const stationList = [`${stationVal}`, `${stationVal}_C`, `${stationVal}_D`];
      const stationCond = stationList.map(s => `station='${s}'`).join(' or ');
      sql = `select ${columns.join(', ')} from "${pkg}" where time >${T0} and time < ${T1} and (${stationCond}) order by time`;
    } else {
      sql = `select ${columns.join(', ')} from "${pkg}" where time >${T0} and time < ${T1} and station = '${stationVal}' order by time`;
    }
    let data = await db.execute(sql); //SAMPLE BY 1m //group BY time(1s)
    let pkgs = current_packages.dataset(data);
    $frm[0].max = pkgs ? pkgs.length : 0;
    return pkgs;
  }

  var keyAll = []

  async function pkg_change() {
    let pkg = $pkg.val()
    let q = await fetch(pkg + '.json').then(v => v.json())
    $prm.html(q.TmParas.map(v => {
      r = document.createElement('option')
      r.data = v
      r.value = v.id;
      r.innerHTML = v.n;
      return r;
    }))

    current_packages = new PKGS(q);
    // 移除自动查询
    // await queryData()
    // $prm.val(keys)
    // 清空之前的数据显示
    $TB.html(`<tr><td style="color: #999" colspan="10">请点击查询按钮获取数据</td></tr>`)
    chart.clear()
  }

  // 移除自动查询功能
  async function timeChange() {
    validateTimeRange();
    // 移除自动查询
    // await queryData()
    // showtable();
    // display_chart()
  }

  function display_chart() {
    chart.clear()
    let selected = $prm.val()
    let len = selected.length
    if (len > 0 && len < CLIMT) {
      chart.showLoading()
      let d = selected.map(k => current_packages.data(k));// (keys[0])
      let names = d.map(v => v.name)
      chart.setOption(option)
      chart.setOption({series: d, title: {text: current_packages.n}, legend: {data: names}})
      chart.hideLoading()
    }
  }

  $pkg.change(pkg_change)
  $prm.change(() => {
    showtable()
    display_chart()
  })

  function showtable(keys, pkgs) {
    let selected = $prm.val()
    keys = keys || (selected.length > 0 ? selected : current_packages.keys);
    pkgs = pkgs || current_packages.values;
    let s = Number($frm.val());
    $TH.html(current_packages.thead(keys));
    if (pkgs && pkgs.length > 0) {
      $TB.html('')
      $pgs.html(`${s}/${pkgs.length}`);
      pkgs.slice(s, s + NLINE).forEach(pkg => $TB.append(pkg.tr(keys)));
    } else {
      $pgs.html(`0/0`);
      $frm[0].max = 0;
      $TB.html(`<tr><td style="color: #F00" colspan="${keys.length + 1}"> 无数据</td></tr>`)
    }
  }

  $frm.change(function () {
    // showtable(keys, pkgs)
    // console.log("frm.change.keys:",keys, pkgs)
    showtable()
  });
  window.onresize = chart.resize;
  chart.getDom().onclick = () => {
    $frm.val(Math.floor(chart.curDataIndex / NLINE) * NLINE);
    showtable();
  }
  chart.on('click', function (e) {
    $frm.val(e.dataIndex);
    showtable()
  })

  function save() {
    a = document.createElement('a');
    a.href = chart.getDataURL({type: 'png', pixelRatio: 1});
    a.download = current_packages.name + '.png';
    a.click();
  }

  function turnPage(nextFlag) {
    let val = Number($frm.val())
    $frm.val(val + (nextFlag ? +NLINE : -NLINE));
    showtable();
  }

  $TB[0].onmousewheel = e => {
    console.log("wheel", e)
    if (e.shiftKey) return
    turnPage(e.wheelDelta < 0)
  };

</script>
</body>
</html>